package com.gitee.aurora.comm;

import com.alibaba.fastjson.JSON;
import com.gitee.aurora.utils.BatchUtil;
import com.gitee.aurora.utils.DataBaseMappingUtil;
import com.gitee.aurora.utils.MapToObj;
import com.gitee.aurora.utils.ResultVO;
import com.gitee.aurora.utils.ResultVOUtils;
import com.sun.istack.internal.NotNull;
import org.apache.commons.lang3.StringUtils;
import org.springframework.jdbc.core.JdbcTemplate;

import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.sql.DataSource;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Pattern;

@Component
public class AuroraJdbcTemplate extends JdbcTemplate {


    /**
     * PO路径
     */
    private static final String PO_PATH = "com.gitee.aurora.po";

    public AuroraJdbcTemplate(DataSource dataSource) {
        super(dataSource);
    }

    public <T> List<T> queryForMapToList(String sql, Class<T> elementType) throws Exception {
        List<T> arrayList = new ArrayList<>();
        List<Map<String, Object>> maps = super.queryForList(sql);
        for (Map<String, Object> m : maps) {
            T t = MapToObj.mapToObj(m, elementType);
            arrayList.add(t);
        }
        return arrayList;
    }

    public <T> List<T> queryForMapToUpperCaseList(String sql, Class<T> elementType) throws Exception {
        List<T> arrayList = new ArrayList<>();
        List<Map<String, Object>> maps = super.queryForList(sql);
        for (Map<String, Object> m : maps) {
            T t = MapToObj.mapToObjUpperCase(m, elementType);
            arrayList.add(t);
        }
        return arrayList;
    }


    public <T> List<T> queryForMapToList(String sql, Object[] objects, int[] types, Class<T> elementType) throws Exception {
        List<T> arrayList = new ArrayList<>();
        List<Map<String, Object>> maps = super.queryForList(sql,objects, types);
        for (Map<String, Object> m : maps) {
            T t = MapToObj.mapToObj(m, elementType);
            arrayList.add(t);
        }
        return arrayList;
    }

    public void execute(String sql, @NotNull List params) {
        if (!StringUtils.isEmpty(sql) || !DataBaseMappingUtil.checkSqlDDL(sql)) {
            this.update(sql, params.toArray());
        }
    }




    public   <T> ResultVO insertOrUpdate(String listStr, String code) throws Exception {
        if (StringUtils.isBlank(listStr)) {
            return ResultVOUtils.error("行数据不能为空");
        }
        // 目前code让对方传PO的类名称
        // 后续可以考虑换成code与Po路径的配置关系,通过配置找
        Class poClass = null;
        try {
            String poClassPath = PO_PATH + code;
            poClass = Class.forName(poClassPath);
        } catch (ClassNotFoundException e) {
            return ResultVOUtils.error("请传入正确数据对象(未找到对应PO对象)");
        }
        List<?> list;
        try{
            list = JSON.parseArray(listStr,poClass);//转集合
        }catch (Exception e){
            try{
                list = Arrays.asList(JSON.parseObject(listStr,poClass));//转对象
            }catch (Exception exception){
                return ResultVOUtils.error("请传入正确的参数格式");
            }
        }
        try {
            return this.insertOrUpdate(list);
        }catch (Exception e){
            e.printStackTrace();
            return ResultVOUtils.error("数据入库失败，请检查必传参数及参数长度");
        }
    }


    public <T> ResultVO insertOrUpdate(List<T> list) throws Exception {
        if (CollectionUtils.isEmpty(list)) {
            return ResultVOUtils.error( "行数据不能为空");
        }

        // 对应表名
        String tableName = null;
        // 表中主键个数
        int primaryNumber = 0;

        // 获取保存对象对应的表和主键
        T data = list.get(0);
        Class dataClass = data.getClass();
        Field[] fieldList = dataClass.getDeclaredFields();
        if (dataClass.isAnnotationPresent(Table.class)) {
            // 获取表名
            Table tableAnn = (Table) dataClass.getAnnotation(Table.class);
            tableName = tableAnn.name();
            // 获取主键字段
            for (Field fd : fieldList) {
                // 判断是否包含主键注解
                if (fd.isAnnotationPresent(Id.class)) {
                    primaryNumber++;
                    break;
                }

            }

        }

        if (StringUtils.isBlank(tableName)) {
            new RuntimeException("请检查数据对象是否为PO,即PO类上必须有Table注解");
        }

        if (primaryNumber == 0) {
            new RuntimeException("请检查PO对象,是否包含主键字段,目前只支持表中包含主键批量更新或者插入");
        }
        StringBuffer errNullMessage;
        StringBuffer errLengthMessage;
        StringBuffer errMessage = new StringBuffer();
        boolean errNum;
        // 检查数据准确性
        for ( int i=0;i<list.size();i++) {
            boolean check = false;
            T data1 = list.get(i);
            Class dataClass1 = data.getClass();
            errNullMessage = new StringBuffer();
            errLengthMessage = new StringBuffer();
            errNum = true;
            T pojo =list.get(i);
            //先拿出S_ID值,判断是否是以另外16家厂商的标识开头，如是，则需校验TAB_AJ_AQ_BAQSYQKDJB的ryzpurl和bmjct字段
            for (Field fd : fieldList) {
                if (fd.getName().equals("sId")){
                    fd.setAccessible(true);
                    String value = fd.get(pojo).toString();
                    if (value.startsWith("TD") ){
                        check=false;
                    }else if (Pattern.compile("^[a-z0-9]").matcher(value).find()){
                        check=false;
                    }else{
                        check = true;
                    }
                }
            }
            for (Field fd : fieldList) {
                if (fd.isAnnotationPresent(Column.class)) {
                    fd.setAccessible(true);
                    Object value = fd.get(pojo);
                    String fdName = fd.getName();
                    Column column = fd.getAnnotation(Column.class);
                    boolean nullable = column.nullable();

                    if (tableName.equals("TAB_AJ_AQ_BAQSYQKDJB")&&(fdName.equals("ryzpurl")||fdName.equals("bmjct"))&&!check){
                        continue;
                    }else{
                        if(errNum == true && null == value && !nullable){
                            errMessage.append("第"+(i+1)+"条数据");
                            errNum = false;
                        }
                        if ( null == value && !nullable) {
                            errNullMessage.append(fdName+"、");
                        }
                        String type = fd.getType().getName();
                        if (!nullable && type.equals("java.lang.String") && value != null) {
                            int length = column.length();
                            if (errNum == true && value.toString().length() > length) {
                                errMessage.append("第"+(i+1)+"条数据");
                                errNum = false;
                            }
                            if (value.toString().length() > length) {
                                errLengthMessage.append(fdName + "长度不能大于" + length+";");
                            }
                        }
                    }
                }
            }
            if(errNullMessage.length()>0){
                errMessage.append(StringUtils.removeEnd(errNullMessage.toString(),"、")).append("字段不能为空;");
            }
            if(errLengthMessage.length()>0){
                errMessage.append(errLengthMessage.toString());
            }
        }
        if(errMessage.length()>0){
            return ResultVOUtils.error( errMessage.toString());
        }
        String finalTableName = tableName;
        ExecutorService executorService = Executors.newFixedThreadPool(4);//多线程执行
        // 保存或者更新数据
        List<List<T>> lists = splitList(list, 3000);//分批处理
        AtomicReference<Boolean> flag = new AtomicReference<>(false);
        CountDownLatch countDownLatch = new CountDownLatch(lists.size());
        lists.forEach(e -> {
            Future<?> future = executorService.submit(() -> BatchUtil.batchSaveData((List<Object>) e, finalTableName, "SJZX"));
            try {
                future.get();
            } catch (InterruptedException | ExecutionException ex) {
                flag.set(true);
                ex.printStackTrace();
            }
            finally {
                countDownLatch.countDown();
            }
        });
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        executorService.shutdown();
        if (flag.get()) {
            return ResultVOUtils.error("数据库异常");
        }
        return ResultVOUtils.success("批量插入成功");
    }


    /**
     * 按指定大小，分隔集合
     * @param list
     * @param <T>
     * @return
     */
    public static <T> List<List<T>> splitList(List<T> list, int len) {
        //判断非空
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyList();
        }
        //声明返回对象存值
        List<List<T>> result = new ArrayList<>();
        int size = list.size();
        //计算循环次数
        int count = (size + len - 1) / len;
        for (int i = 0; i < count; i++) {
            List<T> subList = list.subList(i * len, Math.min((i + 1) * len, size));
            result.add(subList);
        }
        return result;
    }




}
